<?php
namespace Kuoqi\Worker;

/**
 * Class Socket
 * @package Kuoqi
 */
class Socket extends Event {

    /**
     * 服务地址
     * @var string
     * User kuoqi
     * Datetime 2022/3/24 9:06
     * Remark
     */
    protected $server_address = '0.0.0.0';
    /**
     * 服务端口
     * @var string
     * User kuoqi
     * Datetime 2022/3/24 9:06
     * Remark
     */
    protected $server_port = '';
    /**
     * 当前是否有效
     * @var bool
     * User kuoqi
     * Datetime 2022/3/24 9:07
     * Remark
     */
    protected $is_valied = true;
    /**
     * 如果无效的时候展示的错误信息
     * @var string
     * User kuoqi
     * Datetime 2022/3/24 9:08
     * Remark
     */
    protected $errmsg = '';


    /**
     * 是否开启debug
     * @var bool
     * User kuoqi
     * Datetime 2022/3/24 9:34
     * Remark
     */
    protected $debug = true;

    /**
     * 最多可积压的客户端数量
     * @var int
     * User kuoqi
     * Datetime 2022/3/24 9:15
     * Remark
     */
    protected $backlog = 2;

    /**
     * 用于存储当前的服务socket列表
     * @var array
     * User kuoqi
     * Datetime 2022/3/24 9:46
     * Remark
     */
    protected $sockets = array();

    protected $master = null;



    /**
     * 创建一个socket模块
     * Socket constructor.
     * @param $address
     * @param $port
     */
    public function __construct($address,$port){
        $this->server_address = $address;
        $this->server_port = $port;

        if(!($this->master=socket_create(AF_INET, SOCK_STREAM, SOL_TCP))){
            $this->errmsg = "socket_create() failed";
            $this->is_valied = false;
        }
        if(!socket_set_option($this->master, SOL_SOCKET, SO_REUSEADDR, 1)){
            $this->errmsg = "socket_option() failed";
            $this->is_valied = false;
        }
        if(!@socket_bind($this->master,$this->server_address,$this->server_port)){
            $this->errmsg = sprintf('KuoqiWorker warning:socket_bind failed on %s:%s with %s'."\n",
                $this->server_address,
                $this->server_port,
                socket_strerror(socket_last_error())
            );
            $this->is_valied = false;
        }
        //最多将有个积压传入连接排队等待处理。如果连接请求到达时队列已满，则客户端可能会收到一个错误，指示ECONREFUNCE，或者，如果基础协议支持传输，则可能会忽略该请求，以便重试成功。
        //传递给backlogparameter的最大数量在很大程度上取决于底层平台。在Linux上，它很遗憾地被截断为SOMAXCONN。在win32上，如果通过SOMAXCONN，负责套接字的底层服务提供商将把backlog设置为最大合理值。在这个平台上，没有标准的规定来找出实际的积压价值。
        if(!socket_listen($this->master,$this->backlog)){
            $this->errmsg = "socket_listen() failed";
            $this->is_valied = false;
        }
        $this->sockets[] = $this->master;

        //$this->lister->serverConstruct();
    }

    /**
     * 等待子进程中断，防止子进程成为僵尸进程。
     */
    public function __destruct(){
        //pcntl_wait($status); //等待子进程中断，防止子进程成为僵尸进程。
    }



    /**
     * 这是一个模板，在所有继承中，请自己实现本方法的具体信息
     * User kuoqi
     * Datetime 2022/3/24 9:48
     * Remark
     */
    function listen(){
        $this->log('webSocket Listen . you need respire this function to your context');
        sleep(5);
    }

    public function isValied(){
        return $this->is_valied;
    }

    //send message,服务端之间的相互通讯
    public function doSend($client, $msg){
        if(is_array($msg)){
            $msg = json_encode($msg);
        }
        //$msg = $this->frame($msg);
        return @socket_write($client, $msg, strlen($msg));
    }

    /**
     * 获取sockets的列表，只能排除某一个客户端
     * @param $exclude_client
     * @return array
     */
    public function getClients($exclude_client = null){
        $array = array();
        foreach ($this->sockets as $item){
            //排除当前进程
            if($item == $exclude_client){ continue;}
            //排除主进程信息
            if($item==$this->master){ continue;}
            $array[] = $item;
        }
        return $array;
    }

}